//
//  TLRecorderMp3.m
//  TLMp3Recorder
//
//  Created by Telen on 16/6/24.
//  Copyright © 2016年 Telen. All rights reserved.
//

#import "TLRecorderMp3.h"
#import <AudioToolbox/AudioToolbox.h>
#import "lame.h"
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

static const int bufferByteSize = 1600;
static const int sampeleRate = 16000;   //采样率
static const int bitsPerChannel = 16;   //比特率 16 pcm

#pragma mark-
@interface TLRecorderMp3EncodeOperation : NSOperation
@property (nonatomic, assign) BOOL setToStopped;
@property (nonatomic, assign) NSMutableArray *recordQueue;
@property (nonatomic, strong) NSString* filePath;
@property (nonatomic, assign) BOOL keepOldData;
@end

@implementation TLRecorderMp3EncodeOperation
{
    // 全局指针
    lame_t lame;
}
- (void)main
{
    // mp3压缩参数
    lame = lame_init();
    lame_set_in_samplerate(lame, sampeleRate);//采用率，需要与pcm采样一样，保证声音时常相同
    lame_set_num_channels(lame, 1);
    lame_set_out_samplerate(lame, sampeleRate);//采用率，需要与mp3采样一样，保证声音时常相同
    lame_set_brate(lame, bitsPerChannel); //比特率 声音质量控制。128比较高清了
    lame_set_mode(lame, 2); //立体声
    lame_set_quality(lame, 2); //算法
    lame_init_params(lame);
    
    if (!self.keepOldData) {
        [[NSFileManager defaultManager] removeItemAtPath:self.filePath error:nil];
    }
    if (![[NSFileManager defaultManager] fileExistsAtPath:self.filePath]) {
        [[NSFileManager defaultManager] createFileAtPath:self.filePath contents:nil attributes:nil];
    }
    //
    NSFileHandle* handle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
    BOOL dropFirst = YES; //丢到第一个单元录音数据，防止噪音
    
    while (true) {
        @autoreleasepool {
            NSData *audioData = nil;
            @synchronized(_recordQueue){// begin @synchronized
                if (_recordQueue.count > 0) {
                    // 获取队头数据
                    audioData = [_recordQueue objectAtIndex:0];
                    [_recordQueue removeObjectAtIndex:0];
                    //NSLog(@"_recordQueue %@",@(_recordQueue.count));
                }
            }// end @synchronized
            
            if (audioData != nil) {
                
                short *recordingData = (short *)audioData.bytes;
                int pcmLen = (int)audioData.length;
                int nsamples = pcmLen / 2;
                
                unsigned char buffer[pcmLen];
                // mp3 encode
                int recvLen = lame_encode_buffer(lame, recordingData, recordingData, nsamples, buffer, pcmLen);
                // 存文件
                [handle seekToEndOfFile];
                if (dropFirst) {//修复mp3起始的脉冲声音
                    dropFirst = NO;
                }else{
                    [handle writeData:[NSData dataWithBytes:buffer length:recvLen]];
                }
                
            }else{
                if (_setToStopped && _recordQueue.count <= 0) {
                    //NSLog(@"break");
                    break;
                }else{
                    [NSThread sleepForTimeInterval:0.05];
                    //NSLog(@"sleep");
                }
            }
        }
    }
    
    [handle closeFile];
    
    lame_close(lame);
}

@end

#pragma mark-
#define kNumberAudioQueueBuffers 3
#define kBufferDurationSeconds 0.1f
@interface TLAudioReaderMp3 : NSObject
{
    AudioQueueRef				_audioQueue;
    AudioQueueBufferRef			_audioBuffers[kNumberAudioQueueBuffers];
    AudioStreamBasicDescription	_recordFormat;
}
@property (nonatomic, assign) BOOL isRecording;
@property (nonatomic, assign) NSMutableArray *recordQueue;
//- (void) startRecording;
//- (void) stopRecording;
@end

@implementation TLAudioReaderMp3
@synthesize isRecording = _isRecording;
@synthesize recordQueue = _recordQueue;
- (id)init
{
    self = [super init];
    if (self) {
        AudioSessionInitialize(NULL, NULL, NULL, (__bridge void *)(self));
    }
    return self;
}

// 设置录音格式
- (void) setupAudioFormat:(UInt32) inFormatID SampleRate:(int) sampeleRate
{
    memset(&_recordFormat, 0, sizeof(_recordFormat));
    _recordFormat.mSampleRate = sampeleRate;
    
    UInt32 size = sizeof(_recordFormat.mChannelsPerFrame);
    AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, &size, &_recordFormat.mChannelsPerFrame);
    _recordFormat.mFormatID = inFormatID;
    if (inFormatID == kAudioFormatLinearPCM){
        // if we want pcm, default to signed 16-bit little-endian
        _recordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
        _recordFormat.mBitsPerChannel = bitsPerChannel;
        _recordFormat.mBytesPerPacket = _recordFormat.mBytesPerFrame = (_recordFormat.mBitsPerChannel / 8) * _recordFormat.mChannelsPerFrame;
        _recordFormat.mFramesPerPacket = 1;
    }
}

// 回调函数
void inputBufferHandler(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc)
{
    TLAudioReaderMp3 *recorder = (__bridge TLAudioReaderMp3 *)inUserData;
    if (inNumPackets > 0 && recorder.isRecording){
        
        int pcmSize = inBuffer->mAudioDataByteSize;
        char *pcmData = (char *)inBuffer->mAudioData;
        NSData *data = [[NSData alloc] initWithBytes:pcmData length:pcmSize];
        [recorder.recordQueue addObject:data];
        
        AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
    }
}

// 开始录音
- (BOOL) startRecording
{
    OSStatus status = 0;
    status = AudioSessionSetActive(true);
    // category
    UInt32 category = kAudioSessionCategory_PlayAndRecord;
    status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
    if (status) printf("couldn't set audio category!");
    
    // format
    [self setupAudioFormat:kAudioFormatLinearPCM SampleRate:sampeleRate];
    
    // 设置回调函数
    status = AudioQueueNewInput(&_recordFormat, inputBufferHandler, (__bridge void * _Nullable)(self), NULL, NULL, 0, &_audioQueue);
    
    // 创建缓冲器
    for (int i = 0; i < kNumberAudioQueueBuffers; ++i){
        AudioQueueAllocateBuffer(_audioQueue, bufferByteSize, &_audioBuffers[i]);
        AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[i], 0, NULL);
    }
    // 开始录音
    status = AudioQueueStart(_audioQueue, NULL);
    if (status == 0) {
        _isRecording = YES;
    }else{
        _isRecording = NO;
    }
    return _isRecording;
}

// 停止录音
- (void) stopRecording
{
    if (_isRecording) {
        _isRecording = NO;
        AudioQueueStop(_audioQueue, true);
        AudioQueueDispose(_audioQueue, true);
    }
}

@end

#pragma mark-
@implementation TLRecorderMp3
{
    TLAudioReaderMp3 *recorder;
    NSMutableArray *recordingQueue;
    TLRecorderMp3EncodeOperation *mp3EncodeOperation;
    NSOperationQueue *opetaionQueue;
}

- (id)init
{
    self = [super init];
    if (self) {
        
        recordingQueue = [[NSMutableArray alloc] init];
        opetaionQueue = [[NSOperationQueue alloc] init];
        
        recorder = [[TLAudioReaderMp3 alloc] init];
        recorder.recordQueue = recordingQueue;
        
    }
    return self;
}

/**
 返回 -1，默认未配置
 0 用户禁用
 1 用户允许
 */
+ (int)canMicrophone;  //是否开启了录音
{
    __block int bCanRecord = -1;
    if ([[[UIDevice currentDevice] systemVersion] floatValue] > 7)
    {
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) {
            [audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
                if (granted) {
                    bCanRecord = 1;
                }
                else {
                    bCanRecord = 0;
                }
            }];
        }
    }
    return bCanRecord;
}

+ (BOOL)isAuthorizedMicrophone
{
    BOOL allowUseAudio = NO;
    if([[[UIDevice currentDevice] systemVersion] intValue] >=7)
    {
        AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
        allowUseAudio = (AVAuthorizationStatusAuthorized == status);
        
    }else
    {
        allowUseAudio = YES;
    }
    
    return allowUseAudio;
}

//保证文件目录存在
- (BOOL)checkFilePathDirectorExist:(NSString*)filePath
{
    NSString* directorPath = filePath.stringByDeletingLastPathComponent;
    NSURL* dirURL = [NSURL fileURLWithPath:directorPath];
    //
    BOOL isDir = NO;
    NSFileManager* sharedFM = [NSFileManager defaultManager];
    BOOL ret = [sharedFM fileExistsAtPath:dirURL.path isDirectory:&isDir];
    if(!ret)
    {
        NSError* error = nil;
        ret = [sharedFM createDirectoryAtURL:dirURL withIntermediateDirectories:YES attributes:nil error:&error];
        if (error) {
            NSLog(@"Create Dir Error: %@",error);
        }
    }
    if (!ret && !isDir) {
        NSLog(@"File Dir Error: %@",filePath);
    }
    return ret;
}

- (BOOL)start
{
    if ([self checkFilePathDirectorExist:self.filePath]) {
        [recordingQueue removeAllObjects];
        BOOL ret = [recorder startRecording];
        
        if (ret) {
            if (mp3EncodeOperation) {
                mp3EncodeOperation.setToStopped = YES;
                mp3EncodeOperation = nil;
            }
            
            mp3EncodeOperation = [[TLRecorderMp3EncodeOperation alloc] init];
            mp3EncodeOperation.recordQueue = recordingQueue;
            mp3EncodeOperation.filePath = self.filePath;
            mp3EncodeOperation.keepOldData = self.continueRecord;
            [opetaionQueue addOperation:mp3EncodeOperation];
        }
        return ret;
    }
    return NO;
}

- (void)stop
{
    [recorder stopRecording];
    mp3EncodeOperation.setToStopped = YES;
}

- (BOOL)continueRecordToNewFilePath:(NSString *)filePath
{
    if (recorder.isRecording && [self checkFilePathDirectorExist:filePath]) {
        self.filePath = filePath;
        //结束老文件录音
        mp3EncodeOperation.recordQueue = nil;
        mp3EncodeOperation.setToStopped = YES;
        if (mp3EncodeOperation) {
            mp3EncodeOperation.setToStopped = YES;
            mp3EncodeOperation = nil;
        }
        //新文件录音
        mp3EncodeOperation = [[TLRecorderMp3EncodeOperation alloc] init];
        mp3EncodeOperation.recordQueue = recordingQueue;
        mp3EncodeOperation.filePath = self.filePath;
        mp3EncodeOperation.keepOldData = self.continueRecord;
        [opetaionQueue addOperation:mp3EncodeOperation];
        //
        return YES;
    }
    return NO;
}

@end
